package cn.yzfy.common.web;

import cn.yzfy.common.response.ApiResponse;
import cn.yzfy.common.response.annotations.ApiIgnoreWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.Arrays;
import java.util.List;

/**
 * @author 一朝风月
 * @date 2025/9/11 16:45
 * @Code 面向注解面向卿，终无解释再无你
 * @description
 */
@RestControllerAdvice
@Slf4j
public class ApiResponseBodyAdvice implements ResponseBodyAdvice<Object> {
    ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 需要包装的Controller,
     */
    private static final List<String> HANDLE_CONTROLLER_PACKAGE = List.of(
            "cn.yzfy.controller"
    );

    private static final List<String> NOT_HANDLE = Arrays.asList(
            "org.springdoc.webmvc.api.OpenApiWebMvcResource",
            "org.springdoc.webmvc.ui.SwaggerWelcomeActuator",
            "org.springdoc.webmvc.ui.SwaggerWelcomeWebMvc",
            "org.springdoc.webmvc.ui.SwaggerUiHome",
            "org.springdoc.webmvc.ui.SwaggerConfigResource",
            "org.springdoc.webmvc.api.MultipleOpenApiWebMvcResource",
            "springfox.documentation.swagger.web.ApiResourceController",
            "springfox.documentation.swagger2.web.Swagger2Controller");

    /**
     * 实现是否对API接口返回值包装逻辑
     *
     * @param returnType    the return type
     * @param converterType the selected converter type
     * @return 返回false则不包装, 返回true则包装
     */
    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {

        Class<?> declaringClass = returnType.getDeclaringClass();

        // 获取Controller上的注解,如Controller类上使用@ApiIgnoreWrapper忽略包装则跳过
        ApiIgnoreWrapper typeApiIgnoreWrapper =
                declaringClass.getAnnotation(ApiIgnoreWrapper.class);
        if (typeApiIgnoreWrapper != null) {
            return false;
        }

        // 获取Controller方法上的注解,如Controller方法上使用@ApiIgnoreWrapper忽略包装则跳过
        ApiIgnoreWrapper methodApiIgnoreWrapper =
                returnType.getMethodAnnotation(ApiIgnoreWrapper.class);
        if (methodApiIgnoreWrapper != null) {
            return false;
        }

        // 为了不影响其它的Controller,只对我们配置的controller包进行包装
        for (String packageName : HANDLE_CONTROLLER_PACKAGE) {
            if (declaringClass.getPackageName().equals(packageName)) {
                return true;
            }
        }

        return false;
    }

    /**
     * API响应结果包装
     *
     * @param body                  the body to be written
     * @param returnType            the return type of the controller method
     * @param selectedContentType   the content type selected through content negotiation
     * @param selectedConverterType the converter type selected to write to the response
     * @param request               the current request
     * @param response              the current response
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        ApiResponse<Object> apiResponse = new ApiResponse<>();
        apiResponse.setCode(200);
        apiResponse.setPath(request.getURI().getPath());
        apiResponse.setSuccess(Boolean.TRUE);
        apiResponse.setMessage("操作成功");
        apiResponse.setResult(body);

        // 如果返回值是字符串
        if (selectedConverterType.equals(StringHttpMessageConverter.class)) {
            return apiResponse.toString();
        }

        return apiResponse;
    }
}
